import json
import os
import random
from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.util import Inches, Pt

from config.example import out_line, template_id, reference
from config.output_file_config import template_dir, output_dir
from func.generate_section import generate_section_content
from utils.color_manager import invert_rgb, set_font_color
from utils.outline_parser import chapter2md, chapter2json, outline_to_json
from pptx.enum.text import MSO_AUTO_SIZE
from utils.log import logger

def load_template(template_path):
    """
    通过路径返回模板对象
    :param template_path: 模板路径
    :return: 返回模板对象
    """

    return Presentation(template_path)


def get_layout_mapping(prs):
    # 这里应该是一个获取布局映射的方法，具体实现根据实际情况而定
    return {layout.name: idx for idx, layout in enumerate(prs.slide_layouts)}


def remove_all_slides(prs):
    xml_slides = prs.slides._sldIdLst  # 获取所有幻灯片列表
    while len(xml_slides) > 0:
        xml_slides.remove(xml_slides[-1])  # 删除最后一个元素


def check_overlap(shape1, shape2):
    """检查两个形状是否重叠，基于它们的边界框"""
    rect1 = shape1.left, shape1.top, shape1.width, shape1.height
    rect2 = shape2.left, shape2.top, shape2.width, shape2.height

    # 检查矩形是否重叠
    return not (rect1[2] <= rect2[0] or rect1[0] >= rect2[2] or
                rect1[3] <= rect2[1] or rect1[1] >= rect2[3])


def adjust_text_boxes(shapes, ppt_width, ppt_height, extra_spacing=10):
    """统一调整一组重叠的文本框宽度和位置，确保它们不重叠并不超出PPT页面边界"""
    # 获取所有文本框的最大宽度
    max_width = max(shape.width for shape in shapes)

    # 如果最大宽度超过PPT宽度，则调整宽度，使其不超过PPT宽度
    if max_width > ppt_width:
        max_width = ppt_width

    # 计算调整后的水平偏移量
    offset_x = 0

    # 统一调整文本框的宽度和位置
    for shape in shapes:
        shape.width = max_width  # 统一宽度
        shape.left = offset_x  # 设置新的左边距

        # 确保文本框不超出页面的右边界
        if shape.left + shape.width > ppt_width:
            shape.left = ppt_width - shape.width  # 调整文本框左边距，使其不超出右边界

        offset_x += max_width + extra_spacing  # 更新偏移量，确保文本框之间有间距

        # 确保文本框不超出页面的下边界
        if shape.top + shape.height > ppt_height:
            shape.top = ppt_height - shape.height  # 调整文本框上边距，使其不超出下边界


def copy_placeholder_text_to_slide(slide_layout, new_slide):

    """
    复制母版中的占位符文本及其字体属性（颜色、字号、加粗、斜体）到新幻灯片
    :param slide_layout: 布局对象（母版布局）
    :param new_slide: 新的幻灯片对象
    """

    logger.info(f"将占位符文本从布局模板复制到新创建的幻灯片中。进入copy_placeholder_text_to_slide方法")
    for shape in slide_layout.shapes:
        # 打印 shape 的所有属性
        # logger.info(f"shape 的属性: {dir(shape)}")
        logger.info(f"shape 的name: {shape.name}")
        # # 打印 shape 是否是占位符
        # if hasattr(shape, "placeholder_format"):
        #     logger.info(f"shape.placeholder_format.idx: {shape.placeholder_format.idx}")
        # else:
        #     logger.info(f"shape不是文本占位符")
        if shape.has_text_frame:
            print("母版文本: ", shape.text_frame.text)
            # 在新幻灯片中寻找相同类型的形状并填充文本和字体属性
            for new_shape in new_slide.shapes:
                if new_shape.has_text_frame and hasattr(shape, 'placeholder_format'):
                    if new_shape.placeholder_format.idx == shape.placeholder_format.idx:
                        # 逐段、逐字符复制母版文本及其字体属性
                        new_shape.text_frame.text = shape.text_frame.text
                        print(f"复制文本: {shape.text_frame.text} 到新的占位符")
    return new_slide


# def copy_placeholder_text_to_slide(slide_layout, new_slide):
#     """
#     复制母版中的占位符文本到新幻灯片
#     :param slide_layout: 布局对象（母版布局）
#     :param new_slide: 新的幻灯片对象
#     """
#     for shape in slide_layout.shapes:
#         if shape.has_text_frame:
#             print("母版文本: ", shape.text_frame.text)
#             # 在新幻灯片中寻找相同类型的形状并填充文本
#             for new_shape in new_slide.shapes:
#                 if new_shape.has_text_frame and hasattr(shape, 'placeholder_format'):
#                     if new_shape.placeholder_format.idx == shape.placeholder_format.idx:
#                         new_shape.text_frame.text = shape.text_frame.text
#                         print(f"复制文本: {shape.text_frame.text} 到新的占位符")
#     return new_slide


def create_title_slide(prs, title, layout_mapping, font_color):

    """
    创建首页
    :param prs: 模板PPT对象
    :param title: 标题
    :param layout_mapping:一个字典，包含了布局名称与其对应布局 ID 的映射关系。它用来选择幻灯片的布局。
    :param font_color:
    :return:
    """

    logger.info(f"进入创建首页方法")

    # 随机选出一个以title为起始名称的布局
    title_layout_key = random.choice([key for key in layout_mapping.keys() if key.startswith("title")])
    logger.info(f"title_layout_key:{title_layout_key}")

    title_layout_id = layout_mapping[title_layout_key]
    logger.info(f"title_layout_id:{title_layout_id}")

    # 通过布局 ID 来选择具体的布局模板，并赋值给 title_layout。title_layout是幻灯片的布局对象

    title_layout = prs.slide_layouts[title_layout_id]
    logger.info(f"title_layout:{title_layout}")

    # 向演示文稿中添加一张幻灯片，并使用 title_layout 作为该幻灯片的布局模板。
    new_slide = prs.slides.add_slide(title_layout)
    # 将占位符文本从布局模板复制到新创建的幻灯片中。
    new_slide = copy_placeholder_text_to_slide(title_layout, new_slide)

    for shape in new_slide.shapes:
        if shape.has_text_frame and "标题" in shape.text_frame.text:
            shape.text_frame.text = title
            for paragraph in shape.text_frame.paragraphs:
                set_font_color(paragraph, font_color)  # 设置字体颜色
        elif shape.has_text_frame and "智能PPT" in shape.text_frame.text:
            shape.text_frame.text = "AI PPT"
            for paragraph in shape.text_frame.paragraphs:
                set_font_color(paragraph, font_color)  # 设置字体颜色
    return new_slide


def create_content_slide(prs, chapters, layout_mapping, font_color):
    """
    生成目录页
    :param prs:
    :param chapters:
    :param layout_mapping:
    :param font_color:
    :return:
    """

    logger.info(f"进入生成目录页方法")

    #获取章节目录总数
    num = len(chapters)

    # 随机选择一个适配当前目录总数num的目录布局
    content_layout_key = random.choice([key for key in layout_mapping if key.startswith(f"content_{num}")])
    content_layout_id = layout_mapping[content_layout_key]
    print(f"content_layout: {content_layout_key}, content_layout_id: {content_layout_id}")

    content_layout = prs.slide_layouts[content_layout_id]
    content_slide = prs.slides.add_slide(content_layout)
    new_slide = copy_placeholder_text_to_slide(content_layout, content_slide)

    #
    # # 获取幻灯片高度
    # slide_height = prs.slide_height
    #
    # # 初始化left和width,这个left和width是针对于占位符的长宽。
    # left = Inches(1)
    # width = Inches(8)
    # top = Inches(1)
    # spacing = Inches(1)
    # rgb_color = RGBColor(font_color[0], font_color[1], font_color[2])
    #
    # # 初始化存储字体属性的变量
    # # font_size, font_bold, font_color = Pt(18), False, RGBColor(0, 0, 0)
    # for shape in new_slide.shapes:
    #     if shape.has_text_frame:
    #         text = shape.text_frame.text.strip()
    #         if "目录" in text:
    #             print(f"替换目录占位符")
    #             shape.text_frame.text = "目录"
    #             for paragraph in shape.text_frame.paragraphs:
    #                 set_font_color(paragraph, font_color)  # 设置字体颜色
    #         if "content" in text:
    #             print(f"替换content占位符")
    #             shape.text_frame.text = "content"
    #             for paragraph in shape.text_frame.paragraphs:
    #                 set_font_color(paragraph, font_color)  # 设置字体颜色
    #         if "章节标题" in text:
    #             print(f"匹配成功")
    #             left = shape.left
    #             width = shape.width
    #             shape.text_frame.text = ""
    #             sp = shape._element
    #             sp.getparent().remove(sp)  # 移除占位符
    # # 根据章节数量计算间距和初始顶部位置
    # num_chapters = len(chapters)
    # if num_chapters > 0:
    #     total_height = slide_height - Inches(2)  # 留一定空白边距
    #     spacing = total_height / (num_chapters + 1)
    #     top = spacing  # 第一个文本框的动态顶部距离
    #
    # # 将章节标题添加到幻灯片
    # for idx, chapter in enumerate(chapters):
    #     num = idx + 1
    #     chapter_title = chapter["chapter"]
    #
    #     # 计算每个章节的位置
    #     current_top = top + idx * spacing
    #
    #     # 为每个章节添加文本框
    #     textbox = new_slide.shapes.add_textbox(left, current_top, width, spacing)
    #     text_frame = textbox.text_frame
    #     p = text_frame.add_paragraph()
    #     p.text = f"{num}. {chapter_title}"
    #     # 设置字体颜色
    #     p.font.color.rgb = rgb_color
    for num, chapter in enumerate(chapters, start=1):
        chapter_title = chapter["chapter"]
        for shape in new_slide.shapes:
            logger.info(f"开始对章节目录进行操作")
            if f"章节标题{num}" == shape.text_frame.text:
                shape.text_frame.text = chapter_title
                for paragraph in shape.text_frame.paragraphs:
                    set_font_color(paragraph, font_color)  # 设置字体颜色
            else:
                continue


def create_chapter_slides(prs, chapters, layout_mapping, reference, font_color):

    """
    生成章节标题页以及内页
    :param prs: ppt对象
    :param chapters: 章节列表
    :param layout_mapping: 母版映射
    :param reference: 参考文本
    :return:
    """

    logger.info(f"进入生成章节标题页以及内容页方法")
    # 获取PPT页面的宽度和高度
    ppt_width = prs.slide_width
    ppt_height = prs.slide_height

    num = 0
    for chapter in chapters:
        num += 1
        chapter_layout_key = random.choice([key for key in layout_mapping.keys() if key.startswith("chapter")])
        chapter_layout_id = layout_mapping[chapter_layout_key]
        chapter_layout = prs.slide_layouts[chapter_layout_id]

        chapter_slide = prs.slides.add_slide(chapter_layout)
        new_slide = copy_placeholder_text_to_slide(chapter_layout, chapter_slide)

        for shape in new_slide.shapes:
            logger.info(f"开始对章节标题进行操作")
            if shape.has_text_frame:
                if "章节序号" in shape.text_frame.text:
                    shape.text_frame.text = f"{num}"
                    for paragraph in shape.text_frame.paragraphs:
                        set_font_color(paragraph, font_color)  # 设置字体颜色
                elif "章节标题" in shape.text_frame.text:
                    shape.text_frame.text = chapter["chapter"]
                    for paragraph in shape.text_frame.paragraphs:
                        set_font_color(paragraph, font_color)  # 设置字体颜色

        # 将json重新转换成md
        chapter_md = chapter2md(chapter)
        # 生成小节内容，嵌入到大纲当中去
        section = generate_section_content(reference, chapter_md)

        # 将重新生成的大纲转换成json
        chapter = json.loads(chapter2json(section))

        slides = chapter["slides"]

        # 存储文本框的位置，以便后续检查重叠
        shapes_positions = []

        for slide in slides:
            logger.info(f"开始对小节内容进行操作")
            sections = slide["sections"]
            logger.info(f"当前小节的个数有:{len(sections)}")
            logger.info(f"当前小节为:{sections}")
            slide_layout_key = random.choice(
                [key for key in layout_mapping.keys() if key.startswith(f"{len(sections)}_section")])
            slide_layout_id = layout_mapping[slide_layout_key]
            content_layout = prs.slide_layouts[slide_layout_id]
            new_slide = prs.slides.add_slide(content_layout)
            new_slide = copy_placeholder_text_to_slide(content_layout, new_slide)

            for shape in new_slide.shapes:
                if shape.has_text_frame:
                    text = shape.text_frame.text.strip()
                    if "标题" == text:
                        shape.text_frame.text = slide["slide"]
                        for paragraph in shape.text_frame.paragraphs:
                            set_font_color(paragraph, font_color)  # 设置字体颜色
                    i = 1
                    for item in sections:
                        if f"小节标题{i}" == text:
                            shape.text_frame.text = item["section"]
                            for paragraph in shape.text_frame.paragraphs:
                                set_font_color(paragraph, font_color)  # 设置字体颜色
                        if f"小节内容{i}" == text:
                            text_frame = shape.text_frame

                            text_frame.auto_size = MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT
                            shape.text_frame.text = item["section_content"]

                            for paragraph in shape.text_frame.paragraphs:
                                set_font_color(paragraph, font_color)  # 设置字体颜色
                        i += 1

                    # 存储文本框的位置
                    shapes_positions.append(shape)

        # 在所有文本框添加完毕后，检查是否有重叠
        # for i, shape1 in enumerate(shapes_positions):
        #     for shape2 in shapes_positions[i + 1:]:
        #         # 检查是否属于同一幻灯片
        #         slide1 = shape1.part  # 获取shape1所属的幻灯片对象
        #         slide2 = shape2.part  # 获取shape2所属的幻灯片对象
        #         if slide1 != slide2:
        #             continue  # 如果不在同一幻灯片上，跳过
        #         if check_overlap(shape1, shape2):
        #             print(f"重叠检测！\n"
        #                   f"文本框1位置: 左({shape1.left}), 上({shape1.top}), 宽({shape1.width}), 高({shape1.height})\n"
        #                   f"文本框1内容: {shape1.text_frame.text.strip()}\n"
        #                   f"文本框2位置: 左({shape2.left}), 上({shape2.top}), 宽({shape2.width}), 高({shape2.height})\n"
        #                   f"文本框2内容: {shape2.text_frame.text.strip()}\n")
        #
        #             overlapping_shapes = [shape1, shape2]
        #             adjust_text_boxes(overlapping_shapes, ppt_width, ppt_height, extra_spacing=10)
        #             for shape in overlapping_shapes:
        #                 print(
        #                     f"调整后文本框位置: 左({shape.left}), 上({shape.top}), 宽({shape.width}), 高({shape.height})\n"
        #                     f"调整后文本框内容: {shape.text_frame.text.strip()}\n")
    return prs

def create_end_slide(prs, layout_mapping):
    """

    :param prs: ppt对象
    :param layout_mapping: 母版映射
    :return:

    """
    logger.info(f"进入创建结束页方法")

    # 随机选出一个以end为起始名称的布局
    end_layout_key = random.choice([key for key in layout_mapping.keys() if key.startswith("end")])
    logger.info(f"end_layout_key:{end_layout_key}")

    end_layout_id = layout_mapping[end_layout_key]
    logger.info(f"title_layout_id:{end_layout_id}")

    # 通过布局 ID 来选择具体的布局模板，并赋值给 end_layout是幻灯片的布局对象

    end_layout = prs.slide_layouts[end_layout_id]
    logger.info(f"end_layout:{end_layout}")

    # 向演示文稿中添加一张幻灯片，并使用 end_layout 作为该幻灯片的布局模板。
    new_slide = prs.slides.add_slide(end_layout)
    # 将占位符文本从布局模板复制到新创建的幻灯片中。
    new_slide = copy_placeholder_text_to_slide(end_layout, new_slide)

    # for shape in new_slide.shapes:
    #     if shape.has_text_frame and "标题" in shape.text_frame.text:
    #         shape.text_frame.text = title
    #         for paragraph in shape.text_frame.paragraphs:
    #             set_font_color(paragraph, font_color)  # 设置字体颜色
    #     elif shape.has_text_frame and "智能PPT" in shape.text_frame.text:
    #         shape.text_frame.text = "AI PPT"
    #         for paragraph in shape.text_frame.paragraphs:
    #             set_font_color(paragraph, font_color)  # 设置字体颜色
    return new_slide

def generate_presentation(outline, template_id: str, reference: str, name: str):
    """

    :param template_id:
    :param powerpoint_data: outline,大纲内容,此时是
    :param template_color_id: 模板主题色ID
    :param name: 生成的PPT名称
    :return: 生成成功

    """

    """
    
    """

    logger.info(f"进入generate_presentation方法,此处模板调用为{template_id}")
    background_color = 'black'
    # background_color_rgb = (0, 0, 0)  # 黑色
    # background_color_rgb = (255, 255, 0)  # 黄色
    background_color_rgb = (255, 255, 255)  # 白色

    # 保证字体颜色和背景颜色相反
    font_color = invert_rgb(background_color_rgb)

    # 将大纲由md转换成json
    outline = json.loads(outline_to_json(outline))
    logger.info(f"此处大纲转换成的json为:{outline}")

    chapters = outline['chapters']
    title = outline['title']

    # 找寻模板主题
    template_path = os.path.join(template_dir, f'{template_id}.pptx')

    # 检查模板文件是否存在
    if not os.path.exists(template_path):
        logger.error(f"模板文件 '{template_path}' 不存在。")  # 记录错误日志
        raise FileNotFoundError(f"模板文件 '{template_path}' 不存在。")

    prs = load_template(template_path)

    # 初始化 LayoutManager
    # 预定义的模板，里面有很多个幻灯片布局，一页是一个布局，他的名称和ID构成键值对组成layout_mapping
    layout_mapping = get_layout_mapping(prs)
    logger.info(f"当前模板的布局名称如下：{layout_mapping.keys()}")
    logger.info(f"预定义的模板里面的幻灯片布局如下:{layout_mapping}")

    remove_all_slides(prs)  # 清除模板中所有幻灯片
    prs.core_properties.title = title  # 设置PowerPoint 的核心标题


    # 创建首页
    create_title_slide(prs, title, layout_mapping, font_color)

    # 创建目录页
    create_content_slide(prs, chapters, layout_mapping, font_color)

    # 创建章节页和内容页
    create_chapter_slides(prs, chapters, layout_mapping, reference, font_color)

    # 创建结尾页
    create_end_slide(prs, layout_mapping)

    # 保存生成的 PowerPoint 文件
    name = name + ".pptx"
    # 保存的文件路径`  +

    output_file = os.path.join(output_dir, name)
    prs.save(output_file)
    logger.info(f"演示文稿已保存到 '{output_file}'")
    return "生成成功"

if __name__ == "__main__":
    generate_presentation(out_line,template_id,reference,"test")
